# Copyright 2001-2007 by iNeed,Inc. All Rights Reserved.

"""
use cases:

"""
import sys, os, time, unittest
import driver,passenger
from common import *
from sock_asyncore import *
__author__  = "Number Huang<huang.bafeng@iNeed.com>"
__status__  = "developing"
__version__ = "0.1108.13.1"

addressList=[
    (7896,3421,"NO.775 Pusan RD"),
    (7896,3421,"NO.948 Lujiazui ring RD"),
    (9896,5421,"NO.120 East Guilin St.")
]

phoneList=[
    "13817794778",
    "13917841776",
    "13917794778",
    "18917900802",
    "18917900803"
]
class TestPeer():

    def __init__(self,peer):
        self.peer = peer
        self.reporter = Reporter(None)
    def run(self):
        while True:
             cmd = raw_input("cmd(login/publish/exit:")
             if cmd=="login":
                self.login()
             elif cmd=="publish":
                self.publish()
             elif cmd=="exit":
                self.exit()
                break
             else:
                print "error: uncorrect cmd\n"

    def login(self):
        print "username options:\n"
        idx = 0
        for phone in phoneList:
            print "%d: %s\n"%(idx,phone)
            idx += 1
        user = raw_input("username idx (0):")
        if  user=='':
            user = phoneList[0]
        else:
            user = phoneList[int(user)]
        pwd = 'fdd'
        self.peer.logIn(user,pwd,centerID,centerAddr,self.reporter)

    def exit(self):
        self.peer.exit()

    def publish(self):
        #departLocation
        print "Location options:\n"
        idx = 0
        for address in addressList:
            print "%d: %s\n"%(idx,address)
            idx += 1
        idx = raw_input("departureLocation idx (0):")
        if  idx=='':
            departureLocation = addressList[0]
        else:
            departureLocation = addressList[int(idx)]

        idx = raw_input("destinationLocation idx (ignore):")
        if  idx=='':
            destinationLocation = None
        else:
            destinationLocation = addressList[int(idx)]
        departureTime   = raw_input("departureTime (now):")
        if  departureTime=="":
            departureTime = getLocalTime()
        destinationTime = raw_input("destinationTime(ignore):")
        if destinationTime=="":
            destinationTime = None
        if isinstance(self.peer,driver.Driver):
            status = EST_OFFERING
        else:
            status = EST_BOOKING
        timeout = raw_input("timeout seconds (1000):")
        if timeout=="":
            timeout = 1000
        price = None
        self.peer.addService(None,departureLocation,departureTime,status,timeout,destinationLocation,destinationTime,price)

def test2():
    raise ETimeout
if __name__=="__main__":
    def getLtDrivers(self,driverlist):
        """
        callback by ltmatchingEngine
        """
       
        try:
            
            if driverlist:
               #broadcast the request
                for driver in driverlist:
                     phone = driver[EDR_DRIVERID] 
                     extraParams=None
                     self.ltPoolingOrder = None
                     self.poolingID = None
                     if driver.has_key(EDR_LTPOOLINGORDER):
                         self.ltPoolingOrder = driver[EDR_LTPOOLINGORDER]
                         self.poolingID = self.ltPoolingOrder.poolingID
                         self.driverGrid= driver[EDR_DRIVER_GRID]
                         extraParams="poolingID: %d\r\n"%self.poolingID
                         for request in self.ltPoolingOrder.requestQueue:
                             matcher = request.getProperty(ESP_MATCHER)
                             if matcher!=self:
                                 extraParams+="subID: %d\r\n"%matcher.pubID
                                 extraParams+=matcher.getRequestInfo()
                         self.ltPoolingOrder.allRequestInfo = extraParams
                         self.isLongTermPooling = True
                         #get the ltPoolingOrder Info
                     self._checkAvailability(phone,extraParams)
                                    
            else:
                if self.paramDict["departureTime"]-time.time()<configall.SHORTTIME_LIMIT:
                    self._updateLtRequestStatus(EPS_CLOSED)
                    self._useShorterm()
                    self.log.debug("change longterm serach to short term")
                    self._reRun(self.reChooseInterval)
        except:
                 self.log.exception("fail2") 
    
    def _updateLtRequestStatus(self,status): 
        if not self.ltPoolingOrder:
            self.ltRequest.updateStatus(status)
        else:
            for request in self.ltPoolingOrder.requestQueue:
                request.updateStatus(status)
    def _chooseLongTermDriver(self):
        
        self._updateLtRequestStatus(EPS_WAIT_CANDIDATES)
                      
    def _chooseShortTermDriver(self):
            try:
                driver = self.grid.getNextDriver()
                self.poolingID = None
                if not driver and (self.type&EST_POOLING):
                    driver = self.grid.getNextPoolingDriver((self.paramDict["x"],self.paramDict["y"]),(self.paramDict["x2"],self.paramDict["y2"]),self.paramDict["number"])
                    if driver:
                        self.poolingID = driver.getActivePoolingOrderID()
                if driver:
                    phone = driver.getPhone()
                    if self.poolingID:
                        extraParams="poolingID: %d\r\n"%self.poolingID
                    else:
                        extraParams=None
                    self._checkAvailability(phone,extraParams)
                else:
                    self._reRun(self.reChooseInterval)
            except:
                 self.log.exception("fail")
    def _reRun(self,nextTime):
        #unlike postpone, reRun does not change the interval, just set the next executePoint
        self.status = EPS_WAIT_CANDIDATES
        self.executePoint = self.number + nextTime/self.interval
        self.log.debug("%d,no available driver,postpone the task %d seconds"%(self.pubID,self.reChooseInterval))

    def _updateDriverCapability(self):
        """
        refine the capability structure,
        """
        
        phone = self.peerPair["driver"][0]
        if self.ltRequest:
            time1 = self.destTime+configall.SHORTTIME_LIMIT
            self.driverGrid.removeOneLtOffer(self.paramDict["departureTime"],phone)    
            dc = DriverCapability(phone)
            self.grid2.addOneLtOffer(time1,dc)
    def _useShorterm(self):    
           self.search = self._chooseShortTermDriver
           self.reChooseInterval = RECHOOSE_INTERVAL_SHORT #TODO by config file 
           
    def run(self):
        """
        for notify mode, only the checkTimeout works
        """
        if (self.lastTime-self.begin>self.timeout-1) and self.status == EPS_WAIT_CANDIDATES: #or self.loopCount==1:#check by time is eurate in theory but sometimes it see
            self._processTimeout()
            self.loopCount = 0
            self.log.debug("timeout exit_%d_%d"%(self.lastTime,self.begin))
        elif self.forceExit:#
            self.loopCount = 0
            self.log.debug("force exit")
        elif self.number==self.executePoint:#only execute at executePoint
            self.log.debug("Time to search")
            self.search()
        #self.log.debug("loop=%d,execPoint=%d,num=%d,ts=%d"%(self.loopCount,self.executePoint,self.number,self.lastTime-self.begin))        
        self.number += 1
    
    def _getNearByCondition(self):
        """
        @TODO: depends on the actual position and destTime
        """
        
        num = self.refDistance/BENCH_KM_FORCHOOSE
        if self.paramDict["departureTime"]-self.begin>configall.SHORTTIME_LIMIT:
            waitTime = (num+1)*configall.SHORTTIME_LIMIT #@the time before depart that the driver can commit            
        else:#for short term, the waitTime is zero, and the largest range is 2
            waitTime = 0
            if num>2:
                num = 2
        range = self.gridManager.getAdjacentGridsRange(self.gridIdx[0], self.gridIdx[1], num)
        condition={DEPART_GRIDRANGE:range,ARRIVE_LATEST:self.paramDict["departureTime"]-waitTime}
        return condition

    def _addOneLtItem(self,map1,departTime,item):
        timeIdx = self.owner.getTimeIdx(departTime)
        if not map1.has_key(timeIdx):
                map1[timeIdx] = []
        map1[timeIdx].append(request)
    
    def _removeOneLtItem(self,map1,departTime,key): 
        timeIdx = self.owner.getTimeIdx(departTime)
        queue = map1[timeIdx]
        for item in queue:
            if item.getKey()==key:
                  queue.remove(item) 
                  
    def getltRequestQueues(self):
        with AutoLock(self,self.ltlock1):
            curTimeIdx = self.owner.getTimeIdx(time.time())+1
            for key,requestQueue in self.ltPassengerMap.items():
                  if curTimeIdx>=key:
                      del self.ltPassengerMap[key]
                  else:
                      for request in requestQueue:
                            status = request.getStatus()
                            if status==EPS_CLOSED:
                                requestQueue.remove(request)
                            if status==EPS_WAIT_CANDIDATES:
                                pass
                  
                                   
    def addOneLtRequest(self,departTime,request):
        with AutoLock(self,self.ltlock1):
            self._addOneLtItem(self.ltPassengerMap, departTime, request)
                
    def removeOneLtRequest(self,departTime,pubID):
        with AutoLock(self,self.ltlock1):
            self._removeOneLtItem(self.ltPassengerMap, departTime, pubID)
    
    def addOneLtOffer(self,departTime,capability):
        with AutoLock(self,self.ltlock2):
            self._addOneLtItem(self.ltDriverMap, departTime,capability)
                
    def removeOneLtOffer(self,departTime,driverID):
        with AutoLock(self,self.ltlock2):
            self._removeOneLtItem(self.ltDriverMap, departTime, driverID)
     
    def _dumpQueues(self):
        
            
        if self.emptyQueue:
            self.log.debug("check empty (%d,%d)"%(self.row,self.col))
            for driver in self.emptyQueue:
                self.log.debug("e_%s"%driver.getPhone())
        if self.poolingQueue:
            self.log.debug("check pooling (%d,%d)"%(self.row,self.col))
            for driver in self.poolingQueue:
                self.log.debug("p_%s_%d"%(driver.getPhone(),driver.getActivePoolingOrderID()))
    
    def getNextDriver(self):
        with AutoLock(self,self.lock):
            driver = None
            if self.emptyQueue:
               driver =  self.emptyQueue.pop()
            return driver
    
    def getNextPoolingDriver(self,depart,dest,number):
         with AutoLock(self,self.lock):
            result = None
            if self.poolingQueue:
               for driver in  self.poolingQueue:
                   if not driver.isOverDrived(number):
                       depart = self.owner.getTheGrid(*depart).getRowCol()
                       dest   = self.owner.getTheGrid(*dest).getRowCol()
                       if driver.isInTheRooting(depart,dest):
                           result = driver
                           break
            return result  
    
    def addOneDriver(self,driver,isPooling=False):
        with AutoLock(self,self.lock):
             if not driver.isOnCall(): 
                if not isPooling:
                    if driver not in self.emptyQueue:
                
                        self.emptyQueue.insert(0,driver)
                else:
                    if driver not in self.poolingQueue:
                
                        self.poolingQueue.append(driver)
             self.owner._dumpGrids() 
             
    def removeOneDriver(self,driver):
        with AutoLock(self,self.lock):
            #the driver may exist in both queue
            if driver in self.emptyQueue:
                self.emptyQueue.remove(driver)
            
            if driver in self.poolingQueue:
                self.poolingQueue.remove(driver)
    
